#include <Windows.h>
#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <cmath>
#ifdef WIN32
#include <GL/glew.h>
#else
#include <GL/glew.h>
#endif

#include <GL/GL.h>
#include <GL/GLU.h>

//#include "utility.h"
#include "shader.h"

Shader::Shader()
{
}
Shader::Shader (const char * vp_source, const char * fp_source) 
{
	load(vp_source, fp_source);
}

char * Shader::load_source(const char *fn) {

	FILE *fp;
	char *content = NULL;

	int count=0;

	if (fn != NULL) {
		fp = fopen(fn,"rt");

		if (fp != NULL) {
      
      fseek(fp, 0, SEEK_END);
      count = ftell(fp);
      rewind(fp);

			if (count > 0) {
				content = (char *)malloc(sizeof(char) * (count+1));
				count = fread(content,sizeof(char),count,fp);
				content[count] = '\0';
			}
			fclose(fp);
		}
	}
	return content;
}

int Shader::textFileWrite(char *fn, char *s) {

	FILE *fp;
	int status = 0;

	if (fn != NULL) {
		fp = fopen(fn,"w");
		if (fp != NULL) {
			if (fwrite(s,sizeof(char),strlen(s),fp) == strlen(s))
				status = 1;
			fclose(fp);
		}
	}
	return(status);
}

int Shader::get_uniform(const char * uniform_name) const
{
	 return glGetUniformLocation( p, uniform_name );
}
void Shader::set_uniform(const char * uniform_name, int i) const
{
	glUniform1i( glGetUniformLocation( p, uniform_name), i );
}
void Shader::set_uniform(const char * uniform_name, float f) const
{
	glUniform1f( glGetUniformLocation( p, uniform_name), f);
}
void Shader::set_uniform(const char * uniform_name, const float vector4[], unsigned size) const
{
	glUniform4fv(glGetUniformLocation(p, uniform_name), size,  vector4);
}
void Shader::bind()
{
	glUseProgram(p); 
}
void Shader::load(const char * vp_source, const char * fp_source) 
{

	v = glCreateShader(GL_VERTEX_SHADER);
	f = glCreateShader(GL_FRAGMENT_SHADER);

	char * vs = load_source(vp_source);
	char * fs = load_source(fp_source);

	const char * vv = vs;
	const char * ff = fs;

	glShaderSource(v, 1, &vv,NULL);
	glShaderSource(f, 1, &ff,NULL);

	free((void *)vs);free((void *)fs);

	glCompileShader(v);
	glCompileShader(f);

	printShaderInfoLog(v);
	printShaderInfoLog(f);
	//printShaderInfoLog(f2);

	p = glCreateProgram();
	glAttachShader(p,v);
	glAttachShader(p,f);

	glLinkProgram(p);
	printProgramInfoLog(p);

}

int Shader::printOglError(char *file, int line)
{
    //
    // Returns 1 if an OpenGL error occurred, 0 otherwise.
    //
    GLenum glErr;
    int    retCode = 0;

    glErr = glGetError();
    while (glErr != GL_NO_ERROR)
    {
        printf("glError in file %s @ line %d: %s\n", file, line, gluErrorString(glErr));
        retCode = 1;
        glErr = glGetError();
    }
    return retCode;
}

void Shader::printShaderInfoLog(GLuint obj)
{
    int infologLength = 0;
    int charsWritten  = 0;
    char *infoLog;
    glGetShaderiv(obj, GL_INFO_LOG_LENGTH,&infologLength);
    if (infologLength > 0)
    {
        infoLog = (char *)malloc(infologLength);
        glGetShaderInfoLog(obj, infologLength, &charsWritten, infoLog);
		printf("%s\n",infoLog);
        free(infoLog);
    }
}

void Shader::printProgramInfoLog(GLuint obj)
{
    int infologLength = 0;
    int charsWritten  = 0;
    char *infoLog;

    glGetProgramiv(obj, GL_INFO_LOG_LENGTH,&infologLength);
    if (infologLength > 0)
    {
        infoLog = (char *)malloc(infologLength);
        glGetProgramInfoLog(obj, infologLength, &charsWritten, infoLog);
		printf("%s\n",infoLog);
        free(infoLog);
    }
}

void getGLVersion(int * major, int * minor)
{
	const char * verstr = (const char *) glGetString (GL_VERSION) ;
	if ((verstr == NULL) || (sscanf(verstr, "%d.%d", major, minor) != 2))
	{
		* major = * minor = 0;
		fprintf(stderr, "Invalid GL_VERSION format!!! \n");
	}
}
void getGLSLVerstion(int * major, int * minor)
{
	int gl_major, gl_minor;
	getGLVersion(& gl_major, &gl_minor);
	if (gl_major >= 2)
	{
		const char * verstr = (const char *) glGetString (GL_SHADING_LANGUAGE_VERSION);
		if ((verstr == NULL) || (sscanf(verstr, "%d.%d", major, minor) != 2))
		{	
			* major = * minor = 0;
			fprintf(stderr, "Invalid GL_VERSION format!!! \n");
		}
	}
}
